<?php
namespace core\models; //注意：此命名空间名称是从当前网站根目录开始算起，如根目录为'/dir/fc/z'的话，core就是z下面的core目录，组成完整的路径就是/dir/fc/z/core/models
/**
 * 应用程序执行处理
 * Class Application
 * @package z\core\models
 * @author  ZhouHaoHua <53027327@qq.com>
 */
class Application {
    /**
     * 程序执行的开始时间和结束时间
     * @var mixed
     */
    private $startTime, $endTime;
    /**
     * 网站配置变量
     * @var \config
     */
    protected $config;
    /**
     * 控制器的代表参数名
     * @var string
     */
    public $arg_controller_name = 'c';
    /**
     * 动作方法的代表参数名
     * @var string
     */
    public $arg_action_name = 'a';
    /**
     * 当前控制器对象
     * @var \Abstract_Action
     */
    protected $controller = null;
    /**
     * 已经组织好用于输出模板的数据
     * 注意：若此变量无值则不会输出任何模板
     * @var array
     */
    protected $data = array();

    /**
     * 是否是AJAX请求过来的
     * @author lwkai
     * @var bool
     */
    protected $is_ajax = false;

    /**
     * 网站配置参数组
     * @param \config $config
     */
    public function __construct(\config $config) {
        $this->config    = $config;
        $this->startTime = microtime(true);
        //时区设置
        $tz = $this->config->web()->timeZone;
        if(!$tz){
           $tz = 'America/Los_Angeles';
        }
        date_default_timezone_set($tz);
        $this->is_ajax = \Z::Network()->isAjax();
    }

    /**
     * 运行程序
     */
    public function run() {
        $this->serversSet();
        //url路由处理
        $U = new \Url();
        $U->parseUrl();
        //控制器执行
        $this->runController();
        //视图处理
        $this->runView();
    }

    /**
     * 服务器变量统一调整
     */
    private function serversSet() {
        //整合Apache与Nginx服务器变量。
        $_SERVER['REQUEST_URI'] = isset($_SERVER['HTTP_REQUEST_URI']) ? $_SERVER['HTTP_REQUEST_URI'] : $_SERVER['REQUEST_URI'];
    }

    /**
     * 执行控制器程序
     */
    protected function runController() {
        $acn = $this->arg_controller_name;  //$_POST[$this->arg_controller_name]
        $aan = $this->arg_action_name;  //$_POST[$this->arg_action_name]
        $c   = (!empty($_POST[$acn]) ? $_POST[$acn] : (!empty($_GET[$acn]) ? (string) $_GET[$acn] : 'index')); //如$c='index',$mod='index'...(字母数字_格式)
        $a   = (!empty($_POST[$aan]) ? $_POST[$aan] : (!empty($_GET[$aan]) ? (string) $_GET[$aan] : 'index'));
        //控制器的类名与文件名必须一致
        $z_app                       = Z_APP . '/';
        $require_files               = array(); //以下数组顺序不可改变
        $require_files['controller'] = file_exists($z_app . 'controllers/' . $c . '.php')
            ? $z_app . 'controllers/' . $c . '.php'
            : $z_app . 'controllers/' . str_replace(array('_', '-'), '/', $c) . '.php'; //控制器文件
        if (strpos($c, './') !== false || strpos($c, '../') !== false) { //$mod含非法字符要果断die
            throw \Z::Error('含非法字符！');
        }
        if(!file_exists($require_files['controller'])){
            throw \Z::Error('控制器文件不存在！'.$require_files['controller']);
        }
        foreach ($require_files as $_type => $_file) {
            if (file_exists($_file)) {
                require_once($_file);
                if (strpos($_file, $z_app . 'controllers/') === 0) {
                    $_class  = '\\' . $c . '_Action'; //basename($_file, '.php') . '_Action';//echo $mod;exit;
                    $_action = $a;
                    if (!class_exists($_class)) {
                        throw \Z::Error('出错了，请按标准去写控制器！控制器类名称的格式必须是以 _Action 结尾。如：abc_Action或abc_zfd_Action');
                    }
                    $this->controller = new $_class;
                    if (strpos($_action, '__') !== false) {
                        throw \Z::Error($this->arg_action_name . '含非法字符！');
                    }

                    if($this->controller->sessionUse){
                        \core\models\Sessions::Open(
                            $this->controller->sessionSaveType,
                            $this->controller->sessionName,
                            $this->controller->sessionLifeTime,
                            $this->controller->cookiePath,
                            $this->controller->cookieDomain,
                            $this->controller->sessionSaveDir
                        );
                    }
                    //载入网站消息提示类
                    $this->controller->messageStack = \Z::MessageStack();
                    //控制器二级初始化
                    $this->controller->__init();
                    try {
                        //执行具体的控制器方法动作
                        $this->controller->$_action();
                    } catch (Exception404 $e) {
                        $this->notFound($e);
                    }
                }
            } else {
                // 404处理
                $this->notFound(new Exception404($_file));
            }
        }
    }

    /**
     * 404 处理
     * @param Exception404 $e
     * @throws Error
     */
    protected function notFound($e){
        echo $_SERVER['REQUEST_URI']."\n";
        throw \Z::Error('请在当前类的子类中创建一个notFound()方法，在那里处理您的404问题！');
        //$this->runController();
    }
    /**
     * 执行视图处理输出
     */
    public function runView() {
        /* 此方法已经被它的子方法代替
        $c          = $this->controller;
        $this->data = $c->data;
        $viewsPath  = Z_DIR . '/core/views';
        if ($c->data) {
            $this->data['meta']['charset'] = $c->metaCharset;
            $c->metaTitle ? ($this->data['meta']['title'] = $c->metaTitle) : null;
            $c->metaDescription ? ($this->data['meta']['description'] = $c->metaDescription) : null;
            $c->metaKeywords ? ($this->data['meta']['keywords'] = $c->metaKeywords) : null;
        }
        if ($c->tplTop || $c->tplContent || $c->tplBottom) {
            $this->data['tpl']['top']     = $viewsPath . '/' . $c->tplTop;
            $this->data['tpl']['content'] = $viewsPath . '/' . $c->tplContent;
            $this->data['tpl']['bottom'] = $viewsPath . '/' . $c->tplBottom;

            require $viewsPath . '/' . $c->tplMainPage;
        } elseif($this->data) {
            print_r($this->data);
        }*/
    }

    public function __destruct() {
        //return;
        $this->endTime = microtime(true);
        /*if ($this->config->isDebug() === true && (empty($_REQUEST['ajax']) && $this->is_ajax === false)) {//Z_DEBUG
            echo '<pre>';
            echo 'useTime:' . ($this->endTime - $this->startTime);
            //echo print_r($_GET,true);
            //echo print_r($_SERVER,true);
            echo '</pre>';
        }*/
        /*echo '<pre>';
        debug_print_backtrace();
        echo '</pre>';*/
    }
}